package com.zenithmind.common.aspect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zenithmind.common.annotation.DataCache;
import com.zenithmind.common.annotation.DataCacheEvict;
import com.zenithmind.common.config.BloomFilterConfig;
import com.zenithmind.common.result.PageResult;
import com.zenithmind.common.utils.RedisUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 数据缓存切面
 * 实现@DataCache和@DataCacheEvict注解的功能
 */
@Aspect
@Component
public class DataCacheAspect {

    private static final Logger log = LoggerFactory.getLogger(DataCacheAspect.class);

    @Autowired
    private RedisUtils redisUtils;
    
    @Autowired
    private ObjectMapper objectMapper;

    private final ExpressionParser parser = new SpelExpressionParser();
    private final ParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();

    /**
     * 数据缓存切面
     */
    @Around("@annotation(com.zenithmind.common.annotation.DataCache)")
    public Object cacheAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法签名和注解
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        DataCache annotation = method.getAnnotation(DataCache.class);
        
        // 构建缓存键
        String cacheKey = getCacheKey(joinPoint, annotation.prefix(), annotation.key());
        String lockKey = getLockKey(annotation, cacheKey);
        String bloomFilterName = getBloomFilterName(annotation);
        
        // 获取返回类型信息，用于正确反序列化
        Type returnType = method.getGenericReturnType();
        boolean isPageResult = isPageResultType(returnType);
        
        // 首先检查布隆过滤器（如果启用且名称有效）
        if (annotation.useBloomFilter() && StringUtils.hasText(bloomFilterName)) {
            // 检查布隆过滤器之前，确保它已经被初始化了
            // Redisson的RBloomFilter在getBloomFilter()时不会抛错，但在未init就contains()或add()时会
            // 此处的isInBloomFilter内部会getBloomFilter().contains()，如果目标filterName未在BloomFilterConfig中init过，就会报错
            // 所以getBloomFilterName的修改至关重要，它现在只返回已知被初始化的filter
            if (!redisUtils.isInBloomFilter(bloomFilterName, cacheKey)) {
                 log.debug("布隆过滤器 {} 中不存在该键: {}. 返回 null.", bloomFilterName, cacheKey);
                 return null; 
            }
        }
        
        // 查询缓存
        Object cachedValue = redisUtils.get(cacheKey);
        if (cachedValue != null) {
            log.debug("缓存命中: {}", cacheKey);
            
            // 如果是PageResult类型，需要特殊处理反序列化
            if (isPageResult && cachedValue instanceof LinkedHashMap) {
                try {
                    // 将LinkedHashMap转换为JSON字符串
                    String jsonValue = objectMapper.writeValueAsString(cachedValue);
                    // 构建完整的泛型类型信息
                    JavaType javaType = constructJavaType(returnType);
                    // 反序列化为正确的类型
                    return objectMapper.readValue(jsonValue, javaType);
                } catch (JsonProcessingException e) {
                    log.error("反序列化PageResult失败，将执行原始方法: {}", e.getMessage());
                    // 如果反序列化失败，则执行原始方法
                    return joinPoint.proceed();
                }
            }
            
            return cachedValue;
        }
        
        // 处理缓存击穿（如果启用分布式锁）
        if (annotation.useLock()) {
            return handleWithLock(joinPoint, cacheKey, lockKey, annotation, returnType);
        } else {
            return handleWithoutLock(joinPoint, cacheKey, annotation, returnType);
        }
    }
    
    /**
     * 使用分布式锁处理缓存逻辑
     */
    private Object handleWithLock(ProceedingJoinPoint joinPoint, String cacheKey, String lockKey, 
                                 DataCache annotation, Type returnType) throws Throwable {
        // 尝试获取分布式锁
        boolean locked = redisUtils.tryLock(lockKey, 3, 10, TimeUnit.SECONDS);
        if (!locked) {
            // 获取锁失败，短暂休眠后重新查询缓存
            Thread.sleep(100);
            Object cachedValue = redisUtils.get(cacheKey);
            if (cachedValue != null) {
                // 如果是PageResult类型，需要特殊处理反序列化
                if (isPageResultType(returnType) && cachedValue instanceof LinkedHashMap) {
                    try {
                        String jsonValue = objectMapper.writeValueAsString(cachedValue);
                        JavaType javaType = constructJavaType(returnType);
                        return objectMapper.readValue(jsonValue, javaType);
                    } catch (JsonProcessingException e) {
                        log.error("反序列化PageResult失败: {}", e.getMessage());
                        return cachedValue; // 返回原始值，可能会导致类型转换错误
                    }
                }
                return cachedValue;
            }
            // 递归重试
            return handleWithLock(joinPoint, cacheKey, lockKey, annotation, returnType);
        }
        
        try {
            // 双重检查，再次查询缓存
            Object cachedValue = redisUtils.get(cacheKey);
            if (cachedValue != null) {
                // 如果是PageResult类型，需要特殊处理反序列化
                if (isPageResultType(returnType) && cachedValue instanceof LinkedHashMap) {
                    try {
                        String jsonValue = objectMapper.writeValueAsString(cachedValue);
                        JavaType javaType = constructJavaType(returnType);
                        return objectMapper.readValue(jsonValue, javaType);
                    } catch (JsonProcessingException e) {
                        log.error("反序列化PageResult失败: {}", e.getMessage());
                        return cachedValue; // 返回原始值，可能会导致类型转换错误
                    }
                }
                return cachedValue;
            }
            
            // 执行目标方法获取结果
            Object result = joinPoint.proceed();
            
            // 缓存结果
            if (result != null || annotation.cacheNull()) {
                // 如果结果为null且允许缓存null值，使用较短的过期时间
                if (result == null) {
                    redisUtils.set(cacheKey, null, annotation.nullValueExpireTime());
                } else {
                    // 计算过期时间（毫秒）
                    long expireTimeMillis = annotation.timeUnit().toMillis(annotation.expireTime());
                    // 转换为秒
                    long expireTimeSeconds = TimeUnit.MILLISECONDS.toSeconds(expireTimeMillis);
                    redisUtils.set(cacheKey, result, expireTimeSeconds);
                    
                    // 添加到布隆过滤器（如果启用且名称有效）
                    String bloomFilterName = getBloomFilterName(annotation);
                    if (annotation.useBloomFilter() && StringUtils.hasText(bloomFilterName)) {
                        redisUtils.addToBloomFilter(bloomFilterName, cacheKey);
                    }
                }
            }
            
            return result;
        } finally {
            // 释放锁
            redisUtils.unlock(lockKey);
        }
    }
    
    /**
     * 不使用分布式锁处理缓存逻辑
     */
    private Object handleWithoutLock(ProceedingJoinPoint joinPoint, String cacheKey, 
                                   DataCache annotation, Type returnType) throws Throwable {
        // 执行目标方法获取结果
        Object result = joinPoint.proceed();
        
        // 缓存结果
        if (result != null || annotation.cacheNull()) {
            // 如果结果为null且允许缓存null值，使用较短的过期时间
            if (result == null) {
                redisUtils.set(cacheKey, null, annotation.nullValueExpireTime());
            } else {
                // 计算过期时间（毫秒）
                long expireTimeMillis = annotation.timeUnit().toMillis(annotation.expireTime());
                // 转换为秒
                long expireTimeSeconds = TimeUnit.MILLISECONDS.toSeconds(expireTimeMillis);
                redisUtils.set(cacheKey, result, expireTimeSeconds);
                
                // 添加到布隆过滤器（如果启用且名称有效）
                String bloomFilterName = getBloomFilterName(annotation);
                if (annotation.useBloomFilter() && StringUtils.hasText(bloomFilterName)) {
                    redisUtils.addToBloomFilter(bloomFilterName, cacheKey);
                }
            }
        }
        
        return result;
    }
    
    /**
     * 判断返回类型是否为PageResult
     */
    private boolean isPageResultType(Type returnType) {
        if (returnType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) returnType;
            Type rawType = parameterizedType.getRawType();
            return rawType.equals(PageResult.class);
        }
        return false;
    }
    
    /**
     * 构建完整的JavaType，用于反序列化
     */
    private JavaType constructJavaType(Type type) {
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type rawType = parameterizedType.getRawType();
            Type[] typeArguments = parameterizedType.getActualTypeArguments();
            
            // 构建原始类型
            JavaType javaType = objectMapper.constructType(rawType);
            
            // 如果有泛型参数
            if (typeArguments.length > 0) {
                JavaType[] paramTypes = new JavaType[typeArguments.length];
                for (int i = 0; i < typeArguments.length; i++) {
                    paramTypes[i] = objectMapper.constructType(typeArguments[i]);
                }
                // 构建带泛型的类型
                return objectMapper.getTypeFactory().constructParametricType(
                        (Class<?>) rawType, paramTypes);
            }
            return javaType;
        }
        return objectMapper.constructType(type);
    }
    
    /**
     * 数据缓存失效前置通知（执行方法前清除缓存）
     */
    @Before("@annotation(dataCacheEvict) && args(..)")
    public void evictBefore(DataCacheEvict dataCacheEvict) {
        if (dataCacheEvict.beforeInvocation()) {
            // 清除缓存
            evictCache(dataCacheEvict);
        }
    }
    
    /**
     * 数据缓存失效环绕通知（执行方法后清除缓存）
     */
    @Around("@annotation(dataCacheEvict)")
    public Object evictAround(ProceedingJoinPoint joinPoint, DataCacheEvict dataCacheEvict) throws Throwable {
        // 执行目标方法
        Object result = joinPoint.proceed();
        
        // 如果不是在方法执行前清除缓存，则在方法执行后清除
        if (!dataCacheEvict.beforeInvocation()) {
            // 构建缓存键
            String cacheKey = getCacheKey(joinPoint, dataCacheEvict.prefix(), dataCacheEvict.key());
            
            if (dataCacheEvict.allEntries()) {
                // 清除所有以指定前缀开头的缓存
                String prefix = StringUtils.hasText(dataCacheEvict.prefix()) ? 
                        dataCacheEvict.prefix() : 
                        joinPoint.getTarget().getClass().getSimpleName();
                
                // 这里需要根据Redis的实际API实现批量删除逻辑
                // 本例简化处理，仅删除单个键
                redisUtils.delete(cacheKey);
                log.debug("清除缓存: {}", cacheKey);
            } else {
                // 清除指定键的缓存
                redisUtils.delete(cacheKey);
                log.debug("清除缓存: {}", cacheKey);
            }
        }
        
        return result;
    }
    
    /**
     * 清除缓存
     */
    private void evictCache(DataCacheEvict dataCacheEvict) {
        // 在实际应用中，这里需要根据prefix和key构建缓存键
        // 由于这是在Before通知中，无法获取ProceedingJoinPoint
        // 所以只能处理简单情况，复杂情况建议使用Around通知
        
        if (StringUtils.hasText(dataCacheEvict.prefix())) {
            if (dataCacheEvict.allEntries()) {
                // 清除所有以指定前缀开头的缓存
                // 这里需要根据Redis的实际API实现批量删除逻辑
                log.debug("清除前缀为{}的所有缓存", dataCacheEvict.prefix());
            } else if (StringUtils.hasText(dataCacheEvict.key())) {
                // 清除指定键的缓存
                String cacheKey = dataCacheEvict.prefix() + ":" + dataCacheEvict.key();
                redisUtils.delete(cacheKey);
                log.debug("清除缓存: {}", cacheKey);
            }
        }
    }
    
    /**
     * 构建缓存键
     */
    private String getCacheKey(ProceedingJoinPoint joinPoint, String prefix, String key) {
        // 获取类名和方法名
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        
        // 如果没有指定前缀，使用类名
        if (!StringUtils.hasText(prefix)) {
            prefix = className;
        }
        
        // 如果指定了key表达式，解析表达式
        if (StringUtils.hasText(key)) {
            return prefix + ":" + parseKey(key, joinPoint);
        }
        
        // 否则使用方法名和参数值组合
        StringBuilder sb = new StringBuilder(prefix).append(":").append(methodName);
        
        // 添加参数值
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            for (Object arg : args) {
                if (arg != null) {
                    sb.append(":").append(arg.toString());
                }
            }
        }
        
        return sb.toString();
    }
    
    /**
     * 解析SpEL表达式
     */
    private String parseKey(String key, ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();
        String[] parameterNames = nameDiscoverer.getParameterNames(method);
        
        if (parameterNames == null) {
            return key;
        }
        
        EvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        
        Expression expression = parser.parseExpression(key);
        Object value = expression.getValue(context);
        return value != null ? value.toString() : "";
    }
    
    /**
     * 获取分布式锁的键
     */
    private String getLockKey(DataCache annotation, String cacheKey) {
        if (StringUtils.hasText(annotation.lockPrefix())) {
            return annotation.lockPrefix() + ":" + cacheKey;
        }
        return "lock:" + cacheKey;
    }
    
    /**
     * 获取布隆过滤器名称
     */
    private String getBloomFilterName(DataCache annotation) {
        if (StringUtils.hasText(annotation.bloomFilterName())) {
            String explicitName = annotation.bloomFilterName();
            // 仅当名称是预配置的过滤器之一时才返回它
            if (explicitName.equals(BloomFilterConfig.USER_ID_FILTER) ||
                explicitName.equals(BloomFilterConfig.USER_PHONE_FILTER) ||
                explicitName.equals(BloomFilterConfig.USER_EMAIL_FILTER)) {
                return explicitName;
            }
            // 不能直接获取注解所在的方法名作日志，这里做通用警告
            log.warn("DataCache 注解使用了未知的布隆过滤器名称: '{}'. 将不会为此缓存启用布隆过滤器.", explicitName);
            return null; // 指定了未知的名称，禁用布隆过滤
        }
        
        // 如果 useBloomFilter 为 true 但没有指定 bloomFilterName，则不使用布隆过滤器并警告
        // 也不再根据 prefix 自动生成名称，因为那些名称也未被初始化
        if (annotation.useBloomFilter()) {
            // 无法轻易获取注解所在的方法名作日志，这里只做通用警告
            log.warn("DataCache 注解启用了布隆过滤器但未指定 bloomFilterName 或指定的名称无效. 布隆过滤器将不会被使用.");
        }
        return null; // 默认返回null，禁用布隆过滤
    }
} 